package com.xneure.utils.bean;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.lang3.time.FastDateFormat;
import com.xneure.utils.ObjectUtil;

public class BeanMap{
	enum MethodType{
		get,set,nothing;
		@Override
		public String toString(){
			if(this == nothing){
				return "";
			}else{
				return name();
			}
		}
	}

	private static Map<String,Field> fields = new LinkedHashMap<String,Field>(20);
	private static Map<String,Method> methods = new LinkedHashMap<String,Method>(20);
	private static FastDateFormat instance = FastDateFormat.getInstance("yyyy年MM月dd日");
	private Object obj;
	private Object currObj;
	private MethodType headMethod = MethodType.get;

	public BeanMap(Object obj){
		this.obj = obj;
	}

	public void setObj(Object obj){
		this.obj = obj;
	}

	public MethodType getHeadMethod(){
		return headMethod;
	}

	public void setHeadMethod(MethodType headMethod){
		this.headMethod = headMethod;
	}

	public <T> T getMember(String fName,Map<String,T> map,ValueProvide<T> vp){
		try{
			T member = (T)map.get(fName);
			String key = null;
			int point;
			if(null == member && fName.contains(".")){
				Object tmpObj = this.obj;
				String[] tmpStr = fName.split("\\.");
				Class<?> cls = tmpObj.getClass();
				StringBuilder sb = new StringBuilder();
				for(String s:tmpStr){
					point = s.indexOf("[");
					if(point > 1){
						key = s.substring(point + 1,s.length() - 1);
						s = s.substring(0,point);
					}
					member = vp.getMember(cls,s);
					currObj = tmpObj;
					if(null == member){
						break;
					}
					sb.append(".").append(s);
					map.put(sb.substring(1),member);
					tmpObj = vp.getValue(member,currObj);
					if(null == tmpObj){
						break;
					}
					tmpObj = parseValue(tmpObj,key);
					if(null == tmpObj){
						break;
					}
					cls = tmpObj.getClass();
				}
			}else{
				currObj = this.obj;
				point = fName.indexOf("[");
				if(point > 1){
					key = fName.substring(point + 1,fName.length() - 1);
					fName = fName.substring(0,point);
				}
				member = (T)map.get(fName);
				if(null == member){
					member = vp.getMember(this.obj.getClass(),fName);
					if(null != member){
						map.put(fName,member);
					}
				}
			}
			return member;
		}catch(Exception e){
			return null;
		}
	}

	public Object parseValue(Object obj,String key){
		if(key.endsWith("]")){
			int point = key.indexOf("[");
			if(point > 1){
				key = key.substring(point + 1,key.length() - 1);
			}
		}
		if(obj.getClass().isArray()){
			obj = Arrays.asList(obj);
		}
		if(obj instanceof Collection){
			@SuppressWarnings({"unchecked","rawtypes"})
			Iterator<Object> it = ((Collection)obj).iterator();
			StringBuilder sBuilder = new StringBuilder();
			int i = 0;
			int index = null == key ? -1 : Integer.valueOf(key);
			while(it.hasNext()){
				obj = it.next();
				if(index < i || i++ < index){
					continue;
				}
				if(null != obj){
					if(obj.getClass().equals(Date.class)){
						obj = instance.format(obj);
					}
					if(null != obj && obj.toString().length() > 0){
						sBuilder.append(" / ").append(obj);
					}
				}
			}
			if(sBuilder.length() > 3){
				obj = sBuilder.substring(3);
			}else{
				obj = null;
			}
		}else if(obj instanceof Map){
			obj = ((Map<?,?>)obj).get(key);
		}
		return obj;
	}

	public Field getField(String fName){
		return getMember(fName,fields,new ValueProvide<Field>(){
			@Override
			public Object getValue(Field f,Object obj) throws Exception{
				return f.get(obj);
			}

			@Override
			public Field getMember(Class<?> c,String fName) throws Exception{
				while(c.equals(Object.class) == false){
					Field declaredField = c.getDeclaredField(fName);
					if(null == declaredField){
						c = c.getSuperclass();
					}else{
						return declaredField;
					}
				}
				return null;
			}
		});
	}

	public Method getMethod(String mName){
		return getMember(mName,methods,new ValueProvide<Method>(){
			@Override
			public Object getValue(Method t,Object obj) throws Exception{
				return t.invoke(obj);
			}

			@Override
			public Method getMember(Class<?> c,String mName) throws Exception{
				if(ObjectUtil.isNotNull(headMethod)){
					char[] cs = mName.toCharArray();
					cs[0] = Character.toUpperCase(cs[0]);
					mName = headMethod.toString().concat(String.valueOf(cs));
				}
				while(c.equals(Object.class) == false){
					for(Method m:c.getDeclaredMethods()){
						if(m.getName().equals(mName)){
							return m;
						}
					}
					c = c.getSuperclass();
				}
				return null;
			}
		});
	}

	public Object getFieldValue(String fName){
		Field f = getField(fName);
		try{
			if(null != f){
				return parseValue(f.get(currObj),fName);
			}
		}catch(Exception e){
		}
		return null;
	}

	public void setFieldValue(String fName,Object val){
		Field f = getField(fName);
		if(null != f){
			boolean tas = f.isAccessible();
			f.setAccessible(true);
			try{
				f.set(currObj,ObjectUtil.convertValue(val,f.getType()));
			}catch(Exception e){
			}finally{
				f.setAccessible(tas);
			}
		}
	}

	public Object getMethodValue(String mName){
		Method m = getMethod(mName);
		try{
			if(null != m){
				return parseValue(m.invoke(currObj),mName);
			}
		}catch(Exception e){
		}
		return null;
	}

	public void setMethodValue(String mName,Object val){
		this.headMethod = MethodType.set;
		Method m = getMethod(mName);
		try{
			m.invoke(currObj,ObjectUtil.convertValue(val,m.getParameterTypes()[0]));
		}catch(Exception e){
		}
	}

	interface ValueProvide<T>{
		Object getValue(T t,Object obj) throws Exception;

		T getMember(Class<?> c,String tName) throws Exception;
	}
}
